/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.ior;

import java.io.IOException;

import org.omg.CORBA.MARSHAL;
import org.omg.CORBA.OctetSeqHolder;
import org.omg.CORBA_2_3.portable.InputStream;

import com.sun.corba.se.spi.ior.ObjectId;
import com.sun.corba.se.spi.ior.ObjectKey;
import com.sun.corba.se.spi.ior.ObjectKeyFactory;
import com.sun.corba.se.spi.ior.ObjectKeyTemplate;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.impl.orbutil.ORBConstants;

import com.sun.corba.se.impl.ior.JIDLObjectKeyTemplate;
import com.sun.corba.se.impl.ior.POAObjectKeyTemplate;
import com.sun.corba.se.impl.ior.WireObjectKeyTemplate;
import com.sun.corba.se.impl.ior.ObjectIdImpl;
import com.sun.corba.se.impl.ior.ObjectKeyImpl;
import com.sun.corba.se.impl.logging.IORSystemException;

import com.sun.corba.se.impl.encoding.EncapsInputStream;
import sun.corba.EncapsInputStreamFactory;


/**
 * Based on the magic and scid, return the appropriate
 * ObjectKeyTemplate.  Expects to be called with a valid
 * magic.  If scid is not valid, null should be returned.
 */
interface Handler {

  ObjectKeyTemplate handle(int magic, int scid,
      InputStream is, OctetSeqHolder osh);
}

/**
 * Singleton used to manufacture ObjectKey and ObjectKeyTemplate
 * instances.
 *
 * @author Ken Cavanaugh
 */
public class ObjectKeyFactoryImpl implements ObjectKeyFactory {

  public static final int MAGIC_BASE = 0xAFABCAFE;

  // Magic used in our object keys for JDK 1.2, 1.3, RMI-IIOP OP,
  // J2EE 1.0-1.2.1.
  public static final int JAVAMAGIC_OLD = MAGIC_BASE;

  // Magic used only in JDK 1.3.1.  No format changes in object keys.
  public static final int JAVAMAGIC_NEW = MAGIC_BASE + 1;

  // New magic used in our object keys for JDK 1.4, J2EE 1.3 and later.
  // Format changes: all object keys have version string; POA key format
  // is changed.
  public static final int JAVAMAGIC_NEWER = MAGIC_BASE + 2;

  public static final int MAX_MAGIC = JAVAMAGIC_NEWER;

  // Beginning in JDK 1.3.1_01, we introduced changes which required
  // the ability to distinguish between JDK 1.3.1 FCS and the patch
  // versions.  See OldJIDLObjectKeyTemplate.
  public static final byte JDK1_3_1_01_PATCH_LEVEL = 1;

  private final ORB orb;
  private IORSystemException wrapper;

  public ObjectKeyFactoryImpl(ORB orb) {
    this.orb = orb;
    wrapper = IORSystemException.get(orb,
        CORBALogDomains.OA_IOR);
  }

  // XXX The handlers still need to be made pluggable.
  //
  // I think this can be done as follows:
  // 1. Move the Handler interface into the SPI as ObjectKeyHandler.
  // 2. Add two methods to ObjectAdapterFactory:
  //      ObjectKeyHandler getHandlerForObjectKey( ) ;
  //      ObjectKeyHandler getHandlerForObjectKeyTemplate( ) ;
  // 3. Move the implementation of the fullKey handler and the
  //    oktempOnly handler into TOAFactory and POAFactory.
  // 4. Move the ObjectKey impl classes into the impl/oa packages.
  // 5. Create an internal interface
  //      interface HandlerFinder {
  //          ObjectKeyHandler get( int scid ) ;
  //      }
  //    and modify create(InputStream,Handler,OctetSeqHolder)
  //    to take a HandlerFinder instead of a Handler.
  // 6. Modify create( byte[] ) and createTemplate( InputStream )
  //    to create an instance of HandlerFinder: something like:
  //      new HandlerFinder() {
  //          ObjectKeyHandler get( int scid )
  //          {
  //              return orb.getRequestDispatcherRegistry().
  //                  getObjectAdapterFactory( scid ).getHandlerForObjectKey() ;
  //          }
  //      and similarly for getHandlerForObjectKeyTemplate.

  /**
   * This handler reads the full object key, both the oktemp
   * and the ID.
   */
  private Handler fullKey = new Handler() {
    public ObjectKeyTemplate handle(int magic, int scid,
        InputStream is, OctetSeqHolder osh) {
      ObjectKeyTemplate oktemp = null;

      if ((scid >= ORBConstants.FIRST_POA_SCID) &&
          (scid <= ORBConstants.MAX_POA_SCID)) {
        if (magic >= JAVAMAGIC_NEWER) {
          oktemp = new POAObjectKeyTemplate(orb, magic, scid, is, osh);
        } else {
          oktemp = new OldPOAObjectKeyTemplate(orb, magic, scid, is, osh);
        }
      } else if ((scid >= 0) && (scid < ORBConstants.FIRST_POA_SCID)) {
        if (magic >= JAVAMAGIC_NEWER) {
          oktemp = new JIDLObjectKeyTemplate(orb, magic, scid, is, osh);
        } else {
          oktemp = new OldJIDLObjectKeyTemplate(orb, magic, scid, is, osh);
        }
      }

      return oktemp;
    }
  };

  /**
   * This handler reads only the oktemp.
   */
  private Handler oktempOnly = new Handler() {
    public ObjectKeyTemplate handle(int magic, int scid,
        InputStream is, OctetSeqHolder osh) {
      ObjectKeyTemplate oktemp = null;

      if ((scid >= ORBConstants.FIRST_POA_SCID) &&
          (scid <= ORBConstants.MAX_POA_SCID)) {
        if (magic >= JAVAMAGIC_NEWER) {
          oktemp = new POAObjectKeyTemplate(orb, magic, scid, is);
        } else {
          oktemp = new OldPOAObjectKeyTemplate(orb, magic, scid, is);
        }
      } else if ((scid >= 0) && (scid < ORBConstants.FIRST_POA_SCID)) {
        if (magic >= JAVAMAGIC_NEWER) {
          oktemp = new JIDLObjectKeyTemplate(orb, magic, scid, is);
        } else {
          oktemp = new OldJIDLObjectKeyTemplate(orb, magic, scid, is);
        }
      }

      return oktemp;
    }
  };

  /**
   * Returns true iff magic is in the range of valid magic numbers
   * for our ORB.
   */
  private boolean validMagic(int magic) {
    return (magic >= MAGIC_BASE) && (magic <= MAX_MAGIC);
  }

  /**
   * Creates an ObjectKeyTemplate from the InputStream.  Most of the
   * decoding is done inside the handler.
   */
  private ObjectKeyTemplate create(InputStream is, Handler handler,
      OctetSeqHolder osh) {
    ObjectKeyTemplate oktemp = null;

    try {
      is.mark(0);
      int magic = is.read_long();

      if (validMagic(magic)) {
        int scid = is.read_long();
        oktemp = handler.handle(magic, scid, is, osh);
      }
    } catch (MARSHAL mexc) {
      // XXX log this error
      // ignore this: error handled below because oktemp == null
    }

    if (oktemp == null)
    // If we did not successfully construct a oktemp, reset the
    // stream so that WireObjectKeyTemplate can correctly construct the
    // object key.
    {
      try {
        is.reset();
      } catch (IOException exc) {
        // XXX log this error
        // ignore this
      }
    }

    return oktemp;
  }

  public ObjectKey create(byte[] key) {
    OctetSeqHolder osh = new OctetSeqHolder();
    EncapsInputStream is = EncapsInputStreamFactory.newEncapsInputStream(orb, key, key.length);

    ObjectKeyTemplate oktemp = create(is, fullKey, osh);
    if (oktemp == null) {
      oktemp = new WireObjectKeyTemplate(is, osh);
    }

    ObjectId oid = new ObjectIdImpl(osh.value);
    return new ObjectKeyImpl(oktemp, oid);
  }

  public ObjectKeyTemplate createTemplate(InputStream is) {
    ObjectKeyTemplate oktemp = create(is, oktempOnly, null);
    if (oktemp == null) {
      oktemp = new WireObjectKeyTemplate(orb);
    }

    return oktemp;
  }
}
